#pragma once
#include "../common/net.hpp"
#include "../common/message.hpp"
#include <future>

namespace RPC_project
{
    namespace client
    {
        // 让请求与响应进行匹配的模块
        //  针对客⼾端的每⼀条请求进⾏管理，以便于对请求对应的响应做出合适的操作。给每⼀个请求
        //  都设定⼀个请求ID，服务端进⾏响应的时候标识响应针对的是哪个请求（也就是响应信息中会包含请求ID），
        //  因此客⼾端这边我们不管收到哪条请求的响应，将数据存储⼊⼀则hash_map中，以请求ID作为映射，
        //  并向外提供获取指定请求ID响应的阻塞接⼝，这样只要在发送请求的时候知道⾃⼰的请求ID，那么就能获取到⾃⼰想要的响应，⽽不会出现异常

        // 目标：
        // 客户端通过Requestor发送request ，通过Requestor接受response将结果设置进异步存放
        // 实现：
        // 发布前就将发布的request的描述插入hash ，收到response后在hash中找 request的描述将response的结果设置进去
        class Requestor
        {
        public:
            // 对响应进行处理的回调
            using ptr = std::shared_ptr<Requestor>;
            using RequestCallback = std::function<void(const BaseMessage::ptr &)>;
            using AsyncResponse = std::future<BaseMessage::ptr>;

            // 请求的描述
            struct RequestDescribe
            {
                using ptr = std::shared_ptr<RequestDescribe>;

                BaseMessage::ptr _request;
                RType _rtype;
                std::promise<BaseMessage::ptr> _response; // 异步存放response
                RequestCallback _cb;
            };

            // 收到响应的回调处理 ,需要注册给dispatcher
            void onResponse(const BaseConnection::ptr &conn, BaseMessage::ptr &msg)
            { // 接受到了响应msg 用msg中的rid找request的描述，根据两种不同类型进行处理
                std::string rid = msg->Rid();
                RequestDescribe::ptr req_des = getDescribe(rid);
                if (req_des == nullptr)
                {
                    ELOG("收到响应id, %s ,但未找到对应的请求描述", rid.c_str());
                    return;
                }
                if (req_des->_rtype == RType::REQ_ASYNC)
                {
                    // 使用的是promise
                    req_des->_response.set_value(msg);
                }
                else if (req_des->_rtype == RType::REQ_CALLBACK)
                {
                    if (req_des->_cb)
                        req_des->_cb(msg);
                }
                else
                {
                    ELOG("请求类型未知！！！");
                }
                // 处理完该resquest的response结果，进行删除
                delDescribe(rid);
            }

            // 两个异步请求的发送方法
            // new一个描述信息 ，返回关联的future
            bool send(const BaseConnection::ptr &conn, const BaseMessage::ptr &req, AsyncResponse &async_resp)
            {
                // 组织描述
                RequestDescribe::ptr rdp = newDescribe(req, RType::REQ_ASYNC);
                if (rdp.get() == nullptr)
                {
                    ELOG("构建请求描述对象失败");
                    return false;
                }
                // 发送请求
                conn->Send(req);
                async_resp = rdp->_response.get_future();
                return true;
            }
            bool send(const BaseConnection::ptr &conn, const BaseMessage::ptr &req, const RequestCallback &cb)
            {
                RequestDescribe::ptr rdp = newDescribe(req, RType::REQ_CALLBACK, cb);
                if (rdp.get() == nullptr)
                {
                    ELOG("构建请求描述对象失败");
                    return false;
                }
                conn->Send(req);
                return true;
            }


            // 同步发送  ,返回的是BaseMessage类型的response
            bool sync_send(const BaseConnection::ptr &conn, const BaseMessage::ptr &req, BaseMessage::ptr &rsp)
            {
                AsyncResponse res_future;
                bool ret = send(conn, req, res_future);
                if (ret == false)
                {
                    return false;
                }
                rsp = res_future.get(); // get() 是 std::future 的成员函数，用于获取异步操作的结果。阻塞行为：如果结果尚未就绪，调用 get() 会阻塞当前线程，直到异步操作完成。
                return true;
            }

        private:
            RequestDescribe::ptr newDescribe(const BaseMessage::ptr &req, RType rtype, const RequestCallback &cb = RequestCallback())
            {
                std::unique_lock<std::mutex> lock(_mutex);
                RequestDescribe::ptr rd = std::make_shared<RequestDescribe>();
                rd->_request = req;
                rd->_rtype = rtype;
                if (rtype == RType::REQ_CALLBACK && cb)
                {
                    rd->_cb = cb;
                }
                _request_desc.insert(std::make_pair(req->Rid(), rd));
                return rd;
            }
            RequestDescribe::ptr getDescribe(const std::string &rid)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                auto it = _request_desc.find(rid);
                if (it == _request_desc.end())
                {
                    return RequestDescribe::ptr();
                }
                return it->second;
            }
            void delDescribe(const std::string &rid)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                auto it = _request_desc.find(rid);
                if (it == _request_desc.end())
                {
                    return;
                }
                _request_desc.erase(rid);
            }

        private:
            std::mutex _mutex;
            std::unordered_map<std::string, RequestDescribe::ptr> _request_desc; // id 与 RequestDescribe
        };
    }
}